package com.github.defense.starter.gateway.configuration;

import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.exception.NacosException;
import com.github.defense.common.config.DefenseConfig;
import com.github.defense.plugin.base.DefensePlugin;
import com.github.defense.starter.common.configuration.DefenseCommonAutoConfiguration;
import com.github.defense.starter.gateway.filter.GatewayPluginFilter;
import com.github.defense.starter.gateway.handler.JsonExceptionHandler;
import com.github.defense.starter.gateway.launch.DefenseRegisterProcessor;
import com.github.defense.sync.data.api.PluginSubscriber;
import com.github.defense.sync.data.nacos.NacosSyncDataService;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * @Author: lettger
 * @Date: 2021/12/4 上午11:19
 */
@Configuration
@ImportAutoConfiguration(DefenseCommonAutoConfiguration.class)
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class GatewayAutoConfiguration {


    private final ServerProperties serverProperties;
    private final ApplicationContext applicationContext;
    private final ResourceProperties resourceProperties;
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayAutoConfiguration(ServerProperties serverProperties, ResourceProperties resourceProperties,
                                    ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer,
                                    ApplicationContext applicationContext) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 网关注册到defense admin后台
     *
     * @param config
     * @param environment
     * @param applicationEventPublisher
     * @return
     */
    @Bean
    public DefenseRegisterProcessor defenseRegisterProcessor(final DefenseConfig config, final Environment environment, final ApplicationEventPublisher applicationEventPublisher) {
        return new DefenseRegisterProcessor(config, environment, applicationEventPublisher);
    }

    /**
     * defense 后台数据变更 通过Nacos来同步
     *
     * @param nacosConfigProperties
     * @return
     * @throws NacosException
     */
    @Bean
    public NacosSyncDataService nacosSyncDataService(final NacosConfigProperties nacosConfigProperties, ObjectProvider<PluginSubscriber> subscriber) throws NacosException {
        return new NacosSyncDataService(NacosFactory.createConfigService(nacosConfigProperties.assembleConfigServiceProperties()), subscriber.getIfAvailable());
    }

    /**
     * 插件处理器
     *
     * @param plugins
     * @return
     */
    @Bean
    public GatewayPluginFilter pluginGlobalFilter(final ObjectProvider<List<DefensePlugin>> plugins) {
        return new GatewayPluginFilter(Objects.requireNonNull(plugins.getIfAvailable()));
    }


    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
        JsonExceptionHandler exceptionHandler = new JsonExceptionHandler(errorAttributes,
                this.resourceProperties, this.serverProperties.getError(), this.applicationContext);
        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}
